﻿////////////////////////////////////////////////////////////////
///
/// File: Stream.cs
/// Author: Cristian Dittamo
/// Last update: 11 Dec 2009
/// Description: This file contains the definition of the 
///              main classes needed for defining Streams
/// To do: 
/// ------------------------------------------------------------
/// Copyright (c) 2009-2010 Cristian Dittamo (dittamo@di.unipi.it)
/// 
/// The use and distribution terms for this software are 
/// contained in the file named license.txt, which can be found 
/// in the root of this distribution.
/// By using this software in any fashion, you are agreeing to 
/// be bound by the terms of this license.
///
/// You must not remove this notice, or any other, from this
/// software.
/// ------------------------------------------------------------
////////////////////////////////////////////////////////////////
using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace StreamDefs
{
    public enum DistrType
    {
        Row = 1,
        Copy = 2
    }

    #region Stream based on Generics
   
    public abstract class Stream
    {
        public abstract int Count();

        public abstract void Reset();

        public abstract bool MoveNext();

        public abstract new Type GetType();
    }

    /// <summary>
    /// An input stream (readonly), used to feed data to a kernel.
    /// </summary>
    /// <typeparam name="T">The type of the element of the stream.</typeparam>
    public class InputStream<T> : Stream
    {
        protected T[] _data;
        protected object _syncRoot;
        protected int _index;

        public InputStream(int length) 
        {
            _data = new T[length];
            _index = -1;
            _data.Initialize();
        }

        public InputStream(InputStream<T> str) 
        {
            _data = new T[str.Count()];
            _index = -1;
            int x = 0;
            while (str.MoveNext())
                _data[x++] = str.Current;
        }

        public InputStream(T[] array) 
        {
            _data = new T[array.Length];
            _index = -1;

            for (int x = 0; x < _data.Length; x++)
                _data[x] = array[x];
        }

        public InputStream(T[] array, int startIndex, int endIndex) 
        {
            _index = -1;

            if ((startIndex < 0) || (startIndex > endIndex) ||
                (endIndex < 0) || (endIndex < startIndex) ||
                (startIndex == endIndex))
                throw new IndexOutOfRangeException();

            _data = new T[endIndex - startIndex];
            int d = 0;
            for (int x = startIndex; (x < endIndex) && (d < _data.Length); x++)
                _data[d++] = array[x];
        }

        public InputStream(T[,] array) 
        {
            _data = new T[array.Length];
            _index = -1;

            int ind = 0;
            for (int x = 0; x < array.GetLength(0); x++)
                for (int y = 0; y < array.GetLength(1); y++)
                    _data[ind++] = array[x, y];
        }

        public InputStream(InputStream<T>[] nstream) 
        {
            if (nstream.Length == 0)
                throw new ArgumentNullException();

            _index = -1;
            int length = 0;
            int nIndex = 0;
            for (int i = 0; i < nstream.Length; i++)
                length += nstream[i].Count();

            _data = new T[length];

            for (int n = 0; n < nstream.Length; n++)
                while (nstream[n].MoveNext())
                    _data[nIndex++] = nstream[n].Current;
        }

        public T[] ToArray()
        {
            return _data;
        }

        public override int Count() { return _data.Length; }

        public override void Reset()
        {
            _index = -1;
        }

        public override bool MoveNext()
        {
            if ((_index + 1) >= _data.Length)
            {
                _index = -1;
                return false;
            }
            else
            {
                _index++;
                return true;
            }
        }

        public T Current
        {
            get
            {
                try
                {
                    return _data[_index];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
            set
            {
                try
                {
                    _data[_index] = value;
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }

        protected object SyncRoot
        {
            get
            {
                if (this._syncRoot == null)
                {
                    Interlocked.CompareExchange(ref this._syncRoot, new object(), null);
                }
                return this._syncRoot;
            }
        }

        public override Type GetType()
        {
            return typeof(T);
        }

        public static int GetSize()
        {
            Type tt = typeof(T);
            int size = 0;
            if (tt.IsValueType)
            {
                if (tt.IsGenericType)
                {
                    var t = default(T);
                    size = Marshal.SizeOf(t);
                }
                else
                {
                    size = Marshal.SizeOf(tt);
                }

            }
            else
            {
                size = IntPtr.Size;
            }

            return size;
        }

        public List<InputStream<T>> Split(int num_array)
        {
            List<InputStream<T>> arrays = new List<InputStream<T>>(num_array);
            int len_subArr = _data.Length / num_array;

            int tmpIndex = 0;

            for (int ia = 0; ia < num_array; ia++)
            {
                InputStream<T> temp_arr = new InputStream<T>(_data, tmpIndex, tmpIndex + len_subArr);
                tmpIndex += len_subArr;
                arrays.Add(temp_arr);
            }
            return arrays;
        }
    }

    /// <summary>
    /// An output stream (write-only), used to retrieve results computed by a kernel.
    /// </summary>
    /// <typeparam name="T">The type of the element of the stream.</typeparam>
    /// WARNING: initialize correctly the OutPut Stream
    public class OutputStream<T> : Stream
    {
        protected T[] _data;
        protected object _syncRoot;
        protected int _index;

        public OutputStream(int length) 
        {
            _data = new T[length];
            _index = -1;
            _data.Initialize();
        }

        public OutputStream(OutputStream<T> str) 
        {
            _data = new T[str.Count()];
            _index = -1;
            int x = 0;
            while (str.MoveNext())
                _data[x++] = str.Current;
        }

        public OutputStream(T[] array) 
        {
            _data = new T[array.Length];
            _index = -1;

            for (int x = 0; x < _data.Length; x++)
                _data[x] = array[x];
        }

        public OutputStream(T[] array, int startIndex, int endIndex) 
        {
            _index = -1;

            if ((startIndex < 0) || (startIndex > endIndex) ||
                (endIndex < 0) || (endIndex < startIndex) ||
                (startIndex == endIndex))
                throw new IndexOutOfRangeException();

            _data = new T[endIndex - startIndex];
            int d = 0;
            for (int x = startIndex; (x < endIndex) && (d < _data.Length); x++)
                _data[d++] = array[x];
        }

        public OutputStream(T[,] array) 
        {
            _data = new T[array.Length];
            _index = -1;

            int ind = 0;
            for (int x = 0; x < array.GetLength(0); x++)
                for (int y = 0; y < array.GetLength(1); y++)
                    _data[ind++] = array[x, y];
        }

        public OutputStream(OutputStream<T>[] nstream) 
        {
            if (nstream.Length == 0)
                throw new ArgumentNullException();

            _index = -1;
            int length = 0;
            int nIndex = 0;
            for (int i = 0; i < nstream.Length; i++)
                length += nstream[i].Count();

            _data = new T[length];

            for (int n = 0; n < nstream.Length; n++)
                while (nstream[n].MoveNext())
                    _data[nIndex++] = nstream[n].Current;
        }

        public T[] ToArray()
        {
            return _data;
        }

        public override int Count() { return _data.Length; }

        public override void Reset()
        {
            _index = -1;
        }

        public override bool MoveNext()
        {
            if ((_index + 1) >= _data.Length)
            {
                _index = -1;
                return false;
            }
            else
            {
                _index++;
                return true;
            }
        }

        public T Current
        {
            get
            {
                try
                {
                    return _data[_index];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
            set
            {
                try
                {
                    _data[_index] = value;
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }

        protected object SyncRoot
        {
            get
            {
                if (this._syncRoot == null)
                {
                    Interlocked.CompareExchange(ref this._syncRoot, new object(), null);
                }
                return this._syncRoot;
            }
        }

        public override Type GetType()
        {
            return typeof(T);
        }

        public static int GetSize()
        {
            Type tt = typeof(T);
            int size = 0;
            if (tt.IsValueType)
            {
                if (tt.IsGenericType)
                {
                    var t = default(T);
                    size = Marshal.SizeOf(t);
                }
                else
                {
                    size = Marshal.SizeOf(tt);
                }

            }
            else
            {
                size = IntPtr.Size;
            }

            return size;
        }

        public List<OutputStream<T>> Split(int num_array)
        {
            List<OutputStream<T>> arrays = new List<OutputStream<T>>(num_array);
            int len_subArr = _data.Length / num_array;

            int tmpIndex = 0;

            for (int ia = 0; ia < num_array; ia++)
            {
                OutputStream<T> temp_arr = new OutputStream<T>(_data, tmpIndex, tmpIndex + len_subArr);
                tmpIndex += len_subArr;
                arrays.Add(temp_arr);
            }
            return arrays;
        }

        public void Concat(OutputStream<T> nstream)
        {
            int length = nstream.Count();
            int nIndex = 0;

            T[] _nData = new T[_data.Length + length];
            for (int i = 0; i < _data.Length; i++)
                _nData[nIndex++] = _data[i];

            while (nstream.MoveNext())
                _nData[nIndex++] = nstream.Current;

            _data = _nData;
        }

        public void Concat(OutputStream<T>[] nstream)
        {
            int length = 0;
            int nIndex = 0;
            for (int i = 0; i < nstream.Length; i++)
                length += nstream[i].Count();

            T[] _nData = new T[_data.Length + length];
            for (int i = 0; i < _data.Length; i++)
                _nData[nIndex++] = _data[i];

            for (int n = 0; n < nstream.Length; n++)
                while (nstream[n].MoveNext())
                    _nData[nIndex++] = nstream[n].Current;

            _data = _nData;
        }

        /// <summary>
        /// Writes an element on the stream.
        /// </summary>
        /// <param name="item">The element to write.</param>
        public void Write(T item)
        {
            Current = item;
        }
    }

    #endregion


}
